//
//  GradientButton.swift
//  Comon
//
//  Created by Mac on 2022/1/19.
//

import Foundation
import UIKit

public final class GradientButton: UIButton {
  private var cornerRadius: CGFloat = Appearance.cornerRadiusHalfHeight {
    didSet {
      gradientLayer.cornerRadius = cornerRadius
    }
  }
  
  private lazy var appearance = Appearance()
  
  private var gradientLayer: CAGradientLayer {
    guard let gradientLayer = layer as? CAGradientLayer else {
      fatalError("the underlying layer for the button is not CAGradientLayer")
    }
    return gradientLayer
  }
  
  /// default is [CALayer class]. Used when creating the underlying layer for the view.
  public override class var layerClass: AnyClass {
    return CAGradientLayer.self
  }
  
  public override var isEnabled: Bool {
    didSet {
      if isEnabled != oldValue {
        updateAppearance()
      }
    }
  }
  
  public override var isHighlighted: Bool {
    didSet {
      if isHighlighted != oldValue {
        updateAppearance()
      }
    }
  }
  
  public override var isSelected: Bool {
    didSet {
      if isSelected != oldValue {
        updateAppearance()
      }
    }
  }
  
  public override init(frame: CGRect) {
    super.init(frame: frame)
    
    updateAppearance()
  }
  
  public required init?(coder: NSCoder) {
    super.init(coder: coder)
    
    updateAppearance()
  }
  
  public func setGradient(_ gradient: Gradient?, for state: UIControl.State) {
    appearance.setGradient(gradient, for: state)
    
    if state == self.state {
      updateGradient()
    }
  }
  
  private func gradient(for state: UIControl.State) -> Gradient? {
    appearance.gradient(for: state)
  }
  
  private func updateAppearance() {
    updateGradient()
  }
  
  private func updateGradient() {
    var gradientValue: Gradient? = appearance.gradient(for: state)
    if gradientValue == nil {
      gradientValue = appearance.gradient(for: .normal)
    }
    
    guard let currentGradient = gradientValue else {
      gradientLayer.colors = nil
      return
    }
    
    gradientLayer.colors = currentGradient.cgColors
    gradientLayer.locations = currentGradient.locations as [NSNumber]?
    gradientLayer.startPoint = currentGradient.startPoint
    gradientLayer.endPoint = currentGradient.endPoint
  }
  
  public override func layoutSubviews() {
    super.layoutSubviews()
    
    if cornerRadius == Appearance.cornerRadiusHalfHeight &&
        gradientLayer.cornerRadius != bounds.height / 2 {
      gradientLayer.cornerRadius = bounds.height / 2
    }
  }
}

extension UIControl.State: Hashable {}

extension GradientButton {
  final class Appearance {
    public static let cornerRadiusHalfHeight: CGFloat = -1
    public var cornerRadius: CGFloat = cornerRadiusHalfHeight
    
    private var gradientsForState = [UIControl.State: Gradient]()
    
    public func setGradient(_ gradient: Gradient?, for state: UIControl.State) {
      gradientsForState[state] = gradient
    }
    
    public func gradient(for state: UIControl.State) -> Gradient? {
      gradientsForState[state]
    }
  }
}

extension GradientButton {
  public struct Gradient {
    public var colors: [UIColor] {
      didSet {
        if colors.count == 1 {
          colors = Array(repeating: colors[0], count: 2)
        }
      }
    }
    public var locations: [CGFloat]?
    public var startPoint: CGPoint = .zero
    public var endPoint = CGPoint(x: 1, y: 0)
    var cgColors: [CGColor] { colors.map { $0.cgColor } }
    
    public init(colors: [UIColor],
                locations: [CGFloat]? = nil,
                startPoint: CGPoint = .zero,
                endPoint: CGPoint = CGPoint(x: 1, y: 0)) {
      self.colors = colors.count == 1 ? Array(repeating: colors[0], count: 2) : colors
      self.locations = locations
      self.startPoint = startPoint
      self.endPoint = endPoint
    }
  }
}
